// Copyright (c) 2012 Tobias Reimann
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "stdafx.h"
#include "BackgroundService.h"

#include <iostream>
#include <string>
#include <sstream>

#define WIN32_MEAN_AND_LEAN
#include <winsock2.h>
#include <windows.h>

#pragma comment(lib, "Ws2_32.lib")
#define PORT 6811
#define BUFFER 512

using namespace std;

const int WINSCK_VERSION = 2;

SOCKET Socket = INVALID_SOCKET, clientSocket = INVALID_SOCKET;
bool active = true;

BackgroundService::BackgroundService()
{
	
}

void BackgroundService::Start(AtisMaker* am_in)
{
	am = am_in;
	WSADATA wsaData;

	if (WSAStartup(MAKEWORD(WINSCK_VERSION,0), &wsaData)==0)
	{
		// Check if major version is at least WINSOCK_VERSION
		if (LOBYTE(wsaData.wVersion) >= WINSCK_VERSION)
		{
			int port = PORT;
			RunServer(port);
		}
		else
		{
			cerr << "WinSock Version not supported.";
		}
		WSACleanup();
	}
	else
	{
		cerr << "Could not start server.\n";
	}
}

void BackgroundService::Stop()
{
	active = false;
	if (Socket!=INVALID_SOCKET)
		closesocket(Socket);
	
	if (clientSocket!=INVALID_SOCKET)
		closesocket(clientSocket);
}



string BackgroundService::GetHostDescription(const sockaddr_in &socket_addr)
{
	ostringstream stream;
	stream << inet_ntoa(socket_addr.sin_addr) << ":" << ntohs(socket_addr.sin_port);
	return stream.str();
}

void BackgroundService::SetServerSockAddr(sockaddr_in *socket_addr, int port)
{
	socket_addr->sin_family = AF_INET;
	socket_addr->sin_port = htons(port);
	socket_addr->sin_addr.S_un.S_addr = INADDR_ANY;
}

string BackgroundService::GetAtis(string request)
{
	//Typical request:
	//GET /EDDT/E/EDDT%201620Z/26R,26L/26R HTTP/1.1
	//User-Agent: EuroScope
	//Host: localhost:6811

	//parse request
	int start = request.find("GET ") + 4;
	int end = request.find("HTTP/1.1") - 1;
	string clean(request, start, end-start);
	string atis;

	h.ReplaceAll(clean, "%20", " ");
	h.ReplaceAll(clean, "%2D", "-");
	h.ReplaceAll(clean, "%2B", "+");

	h.StrToUpper(clean);

	//extract airport
	start = clean.find("/") + 1;
	end = clean.find(":", start);
	string atisairport(clean, start, end-start);
	//extract atisinfo
	start = clean.find(":", end) + 1;
	end = clean.find(":", start);
	string atiscode(clean, start, end-start);
	//extract metar
	start = clean.find(":", end) + 1;
	end = clean.find(":", start);
	string metar(clean, start, end-start);
	//extract rwyarr
	start = clean.find(":", end) + 1;
	end = clean.find(":", start);
	string arrrwy(clean, start, end-start);
	//extract rwydep
	start = clean.find(":", end) + 1;
	end = clean.find(":", start);
	string deprwy(clean, start, end-start);

	if(clean.find("/") != 0 || clean.find(":") != 5 || clean.find(":", 6) != 7)
	{
		atis = "ATIS-NG Error: Your ATIS Maker URL is invalid. Correct ATIS Maker URL: http://localhost:6811/$atisairport:$atiscode:$metar($atisairport):$arrrwy($atisairport):$deprwy($atisairport)";
	}
	else if(metar.find("$metar") == 0)
	{
		atis = "ATIS-NG Error: No valid metar submitted.";
	}
	else if(arrrwy.length() == 0)
	{
		atis = "ATIS-NG Error: No arrival runway selected.";
	}
	else if(deprwy.length() == 0)
	{
		atis = "ATIS-NG Error: No departure runway selected.";
	}
	else
	{
		atis = am->Generate(atisairport, atiscode, metar, arrrwy, deprwy);
	}
	stringstream ss;
	ss << "HTTP/1.1 200 OK\r\nServer: ATIS-NG\r\nContent-Length: ";
	ss << atis.length();
	ss << "\r\nContent-Type: text/plain\r\n\r\n";
	ss << atis;
	return ss.str();
}

void BackgroundService::HandleConnection(SOCKET clientSocket, const sockaddr_in &socket_addr)
{
	char buffer[BUFFER];

	while(active)
	{
		int result;
		result = recv(clientSocket, buffer, sizeof(buffer), 0);
		if (result==0)
		{ 
			//closed
			break;
		}
		else if (result==SOCKET_ERROR)
		{
			cout << "Socket error on receive.";
		}
		else
		{
			string request(buffer, result);
			string response = GetAtis(request);

			if (send(clientSocket, response.c_str(), response.length(), 0)==SOCKET_ERROR)
			{
				cout << "Socket error on send.";
			}
		}
	}
}


void BackgroundService::RunServer(int port)
{
	sockaddr_in	socket_addr = {0};
	
	if ((Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
	{
		cout << "Failed to create socket.";
		goto failsafe;
	}
	
	SetServerSockAddr(&socket_addr, port);

	if (bind(Socket, reinterpret_cast<sockaddr*>(&socket_addr), sizeof(socket_addr))!=0)
	{
		cout << "Failed to bind to socket.";
		goto failsafe;
	}

	if (listen(Socket, SOMAXCONN)!=0)
	{
		cout << "Failed to listen on socket.";
		goto failsafe;
	}
		
	sockaddr_in clientsocket_addr;
	int clientSockSize = sizeof(clientsocket_addr);
	while(active)
	{
		clientSocket = accept(Socket, reinterpret_cast<sockaddr*>(&clientsocket_addr), &clientSockSize);
	
		if (clientSocket==INVALID_SOCKET)
		{
			cout << "Failed to accept connection.";
		}
		else
		{
			HandleConnection(clientSocket, clientsocket_addr);
		}
		closesocket(clientSocket);
	}

	failsafe:
	if (Socket!=INVALID_SOCKET)	closesocket(Socket);	
	if (clientSocket!=INVALID_SOCKET) closesocket(clientSocket);
}	